Add callable and conditional parallel branches (0075)#175
Merged
chris-colinsky merged 3 commits intoJun 21, 2026
Conversation
A BranchSpec's work may now be given as `call` (an inline async function over the parent state returning a parent-shaped partial) instead of a compiled `subgraph`, and any branch may carry a `when` predicate that skips it at dispatch. `subgraph` becomes optional; exactly one of subgraph/call per branch and no inputs/outputs on a callable branch, else the new compile-time ParallelBranchesInvalidBranchSpec. A node may mix both forms. All-branches-skipped is a valid no-op. A callable branch is the unit of work: it emits one started/completed observer pair keyed by branch_name at the parallel-branches node's own namespace, which both bundled observers render as a single per-branch dispatch span (OTel) / observation (Langfuse) with no inner-node span; a when-skipped branch emits nothing. The NODE event's branch_count is the number of branches that dispatch (when-skipped excluded), while branch_names stays the full declared set. Also fix a pre-existing duplicate Langfuse observation for parallel-branches and fan-out nodes: those emit their own NODE observation, so synthesizing a subgraph-wrapper observation at the same namespace duplicated it. Mirror the OTel observer's existing guard so inner work parents under the single NODE observation.
Advance the spec submodule, __spec_version__, pyproject spec_version, and conformance.toml spec_pin to v0.66.1. This absorbs 0075 (v0.66.0) and the v0.66.1 patch (a call-level-retry Langfuse-mapping clarification the observer already satisfies, no code change). Mark proposal 0075 implemented and regenerate the bundled AGENTS.md. Wire conformance fixtures 073-075: the adapter parses the `call` and `when` branch directives, the fixture schema makes `subgraph` optional, and the pipeline-utilities driver registers the three fixtures. Document callable branches and conditional `when` in the parallel- branches concept page, and extend the example with a pure inline reading-time callable branch and a `when`-gated translation branch so it covers both branch forms.
There was a problem hiding this comment.
Pull request overview
Implements spec proposal 0075 by extending ParallelBranchesNode to support inline-callable branches (BranchSpec(call=...)) and optional conditional dispatch (when=...), along with observer updates (OTel + Langfuse) and a spec pin bump to v0.66.1.
Changes:
- Add callable-branch and
whensupport to parallel branches, including compile-time validation (ParallelBranchesInvalidBranchSpec) and updatedbranch_countsemantics. - Update OTel/Langfuse observers to correctly render callable branches and avoid duplicate Langfuse observations for parallel-branches/fan-out nodes.
- Update conformance wiring, unit tests, docs, examples, and spec version pin references.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/openarmature/graph/parallel_branches.py |
Implements callable branches + conditional dispatch in the runtime node. |
src/openarmature/graph/compiled.py |
Updates NODE event config (branch_count) to exclude when-skipped branches. |
src/openarmature/graph/builder.py |
Adds compile-time validation for subgraph vs call and projection rules. |
src/openarmature/graph/errors.py |
Introduces ParallelBranchesInvalidBranchSpec compile error. |
src/openarmature/graph/__init__.py |
Exports the new compile error symbol. |
src/openarmature/observability/otel/observer.py |
Adds callable-branch span handling for OTel. |
src/openarmature/observability/langfuse/observer.py |
Fixes duplicate observation and parents callable branches correctly. |
tests/unit/test_parallel_branches.py |
Adds unit tests for callable branches, when, and event shapes. |
tests/unit/test_observability_otel.py |
Adds OTel span-shape test for callable branches + when skip. |
tests/unit/test_observability_langfuse.py |
Adds regression test for duplicate parallel-branches observations. |
tests/conformance/adapter.py |
Extends conformance adapter to translate call and when directives. |
tests/conformance/harness/directives.py |
Updates directive model to support call/when. |
tests/conformance/test_pipeline_utilities.py |
Wires fixtures 073–075 into the runner selection. |
docs/concepts/parallel-branches.md |
Documents callable branches and conditional when. |
docs/examples/parallel-branches.md |
Updates example docs to cover new branch forms. |
examples/parallel-branches/main.py |
Updates runnable example to include a callable branch and when-gated branch. |
tests/test_smoke.py |
Updates spec version assertion to v0.66.1. |
src/openarmature/__init__.py |
Updates __spec_version__ to v0.66.1. |
pyproject.toml |
Updates tool.openarmature.spec_version to v0.66.1. |
src/openarmature/AGENTS.md |
Updates bundled agent-guide spec version references. |
conformance.toml |
Advances spec pin and records proposal 0075 as implemented. |
CHANGELOG.md |
Documents proposal 0075 feature and spec pin advance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The callable-branch started/completed pair hard-coded attempt_index=0. Under node-level retry that mis-reports the graph-engine §6 keying tuple and the Langfuse observation's metadata.attempt_index on the second and later attempts. Read current_attempt_index() once at dispatch entry, before the branch chain (which may run its own retries) so the pair shares one value, and thread it through all three dispatches.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements proposal 0075 (spec v0.66.0): inline-callable parallel branches and conditional
when, plus the callable-branch observability the spec later pinned in fixture 110. Advances the pin v0.65.0 to v0.66.1.Callable branches and
whenA
BranchSpec's work may now be given ascall(an inline async function over the parent state returning a parent-shaped partial update) instead of a compiledsubgraph. The contribution is the returned partial directly, merged via the parent reducer with no projection. This makes the primitive adoptable for the "M heterogeneous lightweight parallel calls over shared state" shape (hybrid recall, paired reads) that previously dropped to a hand-rolled concurrent gather, while reusing the existing concurrency, fail-fast cancellation, per-branch failure isolation, and reducer fan-in.A branch gives its work as exactly one of
subgraph/call, and a callable branch declares noinputs/outputs, else the new compile-timeParallelBranchesInvalidBranchSpec. A node may mix both forms freely.Any branch may carry an optional
whenpredicate over the parent state, evaluated once at dispatch. AFalseresult skips the branch entirely: no dispatch, contribution, observer events, or span. All-branches-skipped is a valid no-op.Observability
A callable branch is the unit of work: it emits one started/completed observer pair keyed by
branch_name, which both bundled observers render as a single per-branch dispatch span (OTel) / observation (Langfuse) with no inner-node span beneath it. Awhen-skipped branch emits nothing. This matches spec conformance fixture 110 (v0.70.1), which pins the callable-branch span shape; the behavior is implemented here and the fixture itself wires when the pin reaches v0.70.1 (guarded meanwhile by a mirror unit test).The NODE event's
branch_countis now the number of branches that dispatch (when-skipped excluded), whilebranch_namesstays the full declared set.Also fixes a pre-existing duplicate Langfuse observation for parallel-branches and fan-out nodes: those emit their own NODE observation, so synthesizing a subgraph-wrapper observation at the same namespace duplicated it. The OTel observer already guarded against this; the guard is now mirrored in Langfuse.
Conformance and docs
Pins the spec submodule to v0.66.1 (0075 at v0.66.0 plus the v0.66.1 call-level-retry Langfuse-mapping clarification, which the observer already satisfies). Wires conformance fixtures 073-075. The parallel-branches concept page documents both branch forms and
when, and the example gains a pure inline reading-time callable branch and awhen-gated translation branch.